namespace ClassyBlog.Infrastructure.Specs.Unit
{
    using System;

    using Machine.Specifications;
    using NSubstitute;

    [Subject(typeof(ServiceContainer))]
    public class when_instance_is_accessed
    {
        static IServiceContainer instance;

        Because of = () => instance = ServiceContainer.Current;

        It should_not_be_null = () => instance.ShouldNotBeNull();
    }

    [Subject(typeof(ServiceContainer))]
    public class when_trying_to_register_with_null_factory
    {
        static ServiceContainer container;
        static Exception exception;

        Establish context = () => container = new ServiceContainer();

        Because of = () => exception =
            Catch.Exception(() =>
                container.RegisterAsSingleton((Func<IServiceContainer, object>)null));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentNullException>();
    }

    [Subject(typeof(ServiceContainer))]
    public class when_trying_to_resolve_with_null_service_type
    {
        static ServiceContainer container;
        static Exception exception;

        Establish context = () =>
            container = new ServiceContainer();

        Because of = () => exception =
            Catch.Exception(() => container.Resolve(null));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentNullException>();
    }

    [Subject(typeof(ServiceContainer))]
    public class when_trying_to_resolve_for_null_sevrvice_container
    {
        static IServiceContainer container;
        static Exception exception;

        Establish context = () => container = null;

        Because of = () => exception =
            Catch.Exception(() => container.Resolve<object>());

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentNullException>();
    }

    [Subject(typeof(ServiceContainer))]
    public class when_service_is_not_registered
    {
        static ServiceContainer container;
        static IFileSystem fileSystem;

        Establish context = () => container = new ServiceContainer();

        Because of = () => fileSystem = container.Resolve<IFileSystem>();

        It should_return_default_value = () => fileSystem.ShouldBeNull();
    }

    [Subject(typeof(ServiceContainer))]
    public class when_service_is_registered_as_singleton
    {
        static IFileSystem fileSystem1;
        static IFileSystem fileSystem2;

        static ServiceContainer container;

        Establish context = () =>
        {
            container = new ServiceContainer();
            container.RegisterAsSingleton<IFileSystem>(c => new FileSystem());
        };

        Because of = () =>
        {
            fileSystem1 = container.Resolve<IFileSystem>();
            fileSystem2 = container.Resolve<IFileSystem>();
        };

        It should_return_the_exact_same_instance_for_consequent_request = () =>
            fileSystem1.ShouldBeTheSameAs(fileSystem2);
    }

    [Subject(typeof(ServiceContainer))]
    public class when_service_is_registered_as_transient
    {
        static IFileSystem fileSystem1;
        static IFileSystem fileSystem2;

        static ServiceContainer container;

        Establish context = () =>
        {
            container = new ServiceContainer();
            container.RegisterAsTransient<IFileSystem>(c => new FileSystem());
        };

        Because of = () =>
        {
            fileSystem1 = container.Resolve<IFileSystem>();
            fileSystem2 = container.Resolve<IFileSystem>();
        };

        It should_return_different_instance_for_consequent_request = () =>
            fileSystem1.ShouldNotBeTheSameAs(fileSystem2);
    }

    [Subject(typeof(ServiceContainer))]
    public class when_service_container_disposed
    {
        static ServiceContainer container;
        static IDisposable disposible;

        Establish context = () =>
        {
           disposible = Substitute.For<IDisposable>();
           container = new ServiceContainer();
           container.RegisterAsSingleton(c => disposible);
        };

        Because of = () =>
        {
            container.Resolve<IDisposable>();
            container.Dispose();
        };

        It should_dispose_singleton_services = () =>
            disposible.Received().Dispose();
    }
}